package com.codeduck.config;

import com.zaxxer.hikari.HikariDataSource;
import org.apache.shardingsphere.driver.api.ShardingSphereDataSourceFactory;
import org.apache.shardingsphere.infra.config.RuleConfiguration;
import org.apache.shardingsphere.infra.config.algorithm.ShardingSphereAlgorithmConfiguration;
import org.apache.shardingsphere.sharding.api.config.ShardingRuleConfiguration;
import org.apache.shardingsphere.sharding.api.config.rule.ShardingTableRuleConfiguration;
import org.apache.shardingsphere.sharding.api.config.strategy.keygen.KeyGenerateStrategyConfiguration;
import org.apache.shardingsphere.sharding.api.config.strategy.sharding.StandardShardingStrategyConfiguration;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.*;

/**
 * 分库分表主配置
 *
 * @author: codeduck@163.com
 * @since: 2021-06-03
 */
//@Configuration
public class ShardingDatabasesAndTablesConfig {

    /**
     * 需要手动配置事务管理器
     */
//    @Bean
    public DataSourceTransactionManager transactionManager(@Qualifier("dataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

//    @Bean(name = "dataSource")
//    @Primary
    public DataSource dataSource() throws SQLException {
        ShardingRuleConfiguration shardingRuleConfig = createShardingRuleConfiguration();
        Collection<RuleConfiguration> configurations = new LinkedList<>();
        configurations.add(shardingRuleConfig);
        return ShardingSphereDataSourceFactory.createDataSource(createDataSourceMap(), configurations, new Properties());
    }

    private ShardingRuleConfiguration createShardingRuleConfiguration() {
        ShardingRuleConfiguration result = new ShardingRuleConfiguration();
        // 配置分片表规则列表
        result.getTables().add(getOrderTableRuleConfiguration());
        // 绑定表规则列表
        result.getBindingTableGroups().add("t_order");
        // 设置分库策略
        result.setDefaultDatabaseShardingStrategy(new StandardShardingStrategyConfiguration("user_id", "standard_test_db"));
        // 设置分表策略
        result.setDefaultTableShardingStrategy(new StandardShardingStrategyConfiguration("order_id", "standard_test_tbl"));
        // 分片算法名称和配置
        Properties propsForDb = new Properties();
        propsForDb.setProperty("algorithm-expression", "ds_${user_id % 2}");
        result.getShardingAlgorithms() .put("standard_test_db", new ShardingSphereAlgorithmConfiguration("INLINE", propsForDb));

        Properties propsForTb = new Properties();
        propsForTb.setProperty("algorithm-expression", "t_order_${order_id % 3}");
        result.getShardingAlgorithms() .put("standard_test_tbl", new ShardingSphereAlgorithmConfiguration("INLINE", propsForTb));
        // 自增列生成算法名称和配置
        result.getKeyGenerators().put("snowflake", new ShardingSphereAlgorithmConfiguration("SNOWFLAKE", getProperties()));
        return result;
    }

    /**
     * 生成分表规则
     */
    private ShardingTableRuleConfiguration getOrderTableRuleConfiguration() {
        // 配置分表规则
        ShardingTableRuleConfiguration result = new ShardingTableRuleConfiguration("t_order", "ds_${0..1}.t_order_${0..2}");
        // 使用雪花算法生成业务主键
        result.setKeyGenerateStrategy(new KeyGenerateStrategyConfiguration("id", "snowflake"));
        return result;
    }

    private static Properties getProperties() {
        Properties properties = new Properties();
        properties.setProperty("worker-id", "123");
        properties.setProperty("sql.show", "true");
        return properties;
    }


    /**
     * 数据源配置
     */
    private Map<String, DataSource> createDataSourceMap() {
        Map<String, DataSource> dataSourceMap = new HashMap<>(16);

        // 配置第一个数据源
        HikariDataSource ds_0 = new HikariDataSource();
        ds_0.setDriverClassName("com.mysql.cj.jdbc.Driver");
        ds_0.setJdbcUrl("jdbc:mysql://192.168.2.123:3306/springboot-demo?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8");
        ds_0.setUsername("root");
        ds_0.setPassword("123456");

        // 配置第二个数据源
        HikariDataSource ds_1 = new HikariDataSource();
        ds_1.setDriverClassName("com.mysql.cj.jdbc.Driver");
        ds_1.setJdbcUrl("jdbc:mysql://192.168.2.124:3306/springboot-demo?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8");
        ds_1.setUsername("root");
        ds_1.setPassword("123456");

        dataSourceMap.put("ds_0", ds_0);
        dataSourceMap.put("ds_1", ds_1);
        return dataSourceMap;
    }
}
